Utilizando Dados do Instituto Nacional de Meteorologia (INMET), busque pelas informações de precipitação e temperatura para os estados federativos do Brasil (UF) e responda às seguintes questões:
Questão 1 - Quais as 5 estações meteorológicas que apresentaram maior precipitação acumulada do ano de 2020?
Questão 2 - E quais as 5 estações com menor precipitação acumulada de 2020?
Questão 3 - Plote um gráfico, um para cada estado, com a temperatura média mensal dos últimos 12 meses.
Questão 4 (Bônus): Análise exploratória livre do dataset. Fique à vontade para trazer aqui alguma análise que considere relevante - com base no dataset apresentado
#Importando bibliotecas a serem utilizadas nessa análise:
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import json
import plotly.express as px
import requests
%matplotlib inline
Ao analisarmos as opções de API não há uma opção direta para a obter todos os dados de todas as estações. Sendo assim, iremos primeiro obter os dados das estações e depois iterar por estação para pegar todos os dados. Para mais informações do API do INMET só acessar esse link
#Pegando os dados de todas as estações através da API do INMET e mostramos as primeiras linhas dos dados coletados
r = requests.get('https://apitempo.inmet.gov.br/estacoes/T')
estacoes = r.json()
df_est = pd.json_normalize(estacoes)
df_est.head()
| CD_OSCAR | DC_NOME | FL_CAPITAL | DT_FIM_OPERACAO | CD_SITUACAO | TP_ESTACAO | VL_LATITUDE | CD_WSI | CD_DISTRITO | VL_ALTITUDE | SG_ESTADO | SG_ENTIDADE | CD_ESTACAO | VL_LONGITUDE | DT_INICIO_OPERACAO | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0-2000-0-86765 | ABROLHOS | N | None | Pane | Automatica | -17.96305555 | 0-76-0-2906907000000408 | 04 | 20.93 | BA | INMET | A422 | -38.70333333 | 2008-07-20T21:00:00.000-03:00 |
| 1 | 0-2000-0-81755 | ACARAU | N | None | Pane | Automatica | -3.1211111 | 0-76-0-2300200000000446 | 03 | 67.15 | CE | INMET | A360 | -40.08722221 | 2009-04-21T21:00:00.000-03:00 |
| 2 | 0-2000-0-86827 | AFONSO CLAUDIO | None | None | Pane | Automatica | -20.10416666 | 0-76-0-3200102000000478 | 06 | 507.48 | ES | INMET | A657 | -41.10694444 | 2011-09-23T21:00:00.000-03:00 |
| 3 | 0-2000-0-86686 | AGUA BOA | N | None | Pane | Automatica | -14.01638888 | 0-76-0-5100201000000157 | 09 | 440 | MT | INMET | A908 | -52.21166666 | 2006-12-15T21:00:00.000-03:00 |
| 4 | 0-2000-0-86812 | AGUA CLARA | N | None | Operante | Automatica | -20.44444444 | 0-76-0-5000203000000463 | 07 | 323.63 | MS | INMET | A756 | -52.87583332 | 2010-08-13T21:00:00.000-03:00 |
Das informações disponíveis a que mais nos interessa seria o código da estação, para iteração na API. Então vamos isolá-la e fazer uma exploração básica para checar se a API se comportou como esperamos
print('Número de linhas do DataFrame: {}'.format( df_est.shape[0]))
print('Número de estações únicas: {}'.format(df_est['CD_ESTACAO'].nunique()))
Número de linhas do DataFrame: 605 Número de estações únicas: 605
O resultado acima demonstra que cada linha representa apenas uma estação, como esperado. Sendo assim, vamos gerar a lista para de iteração para obter os dados do ano de 2020 para todas as estações
cod_est = df_est['CD_ESTACAO'].unique() #Gerando uma série com todas as estações
df = pd.DataFrame() #Iniciando um DF vazio para ir adicionando os dados obtidos do API do INMET
#Iterando para cada estação em cod_est, modificando a URL de chamada,
#assim pegando os dados de 2020 para cada estação e compilando em um DataFrame único
for codigo in cod_est[:]:
url = f'https://apitempo.inmet.gov.br/estacao/diaria/2020-01-01/2020-12-31/{codigo}'
r = requests.get(url)
brasil = r.json()
df = df.append(pd.json_normalize(brasil), ignore_index = True)
df.head()
| UMID_MED | DT_MEDICAO | DC_NOME | UMID_MIN | TEMP_MED | CHUVA | VL_LATITUDE | TEMP_MIN | TEMP_MAX | UF | VEL_VENTO_MED | CD_ESTACAO | VL_LONGITUDE | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | None | 2020-01-01 | ABROLHOS | None | 25.6 | None | -17.96305555 | 23.8 | 26.7 | BA | 6.9 | A422 | -38.70333333 |
| 1 | None | 2020-01-02 | ABROLHOS | None | 26.6 | 1 | -17.96305555 | 23.8 | 27.4 | BA | 10 | A422 | -38.70333333 |
| 2 | None | 2020-01-03 | ABROLHOS | None | 26.5 | 0 | -17.96305555 | 26 | 27.2 | BA | 9.9 | A422 | -38.70333333 |
| 3 | None | 2020-01-04 | ABROLHOS | None | 26 | 11 | -17.96305555 | 23.4 | 27.2 | BA | 8.9 | A422 | -38.70333333 |
| 4 | None | 2020-01-05 | ABROLHOS | None | 26.2 | 0 | -17.96305555 | 25.7 | 26.9 | BA | 9.6 | A422 | -38.70333333 |
Nos próximos passos irei começar a fazer as análises exploratórias para responder as questões 1 e 2
De acordo com o manual do API do INMET a coluna CHUVA é relativa a PRECIPITAÇÃO TOTAL, DIARIO (AUT) em mm
#Abaixo crio uma coluna com os nomes dos meses, que será útil em análises posteriores
df['MES'] = df['DT_MEDICAO'].apply(lambda x: x.split('-')[1])
df['CHUVA'].unique()
array([None, '1', '0', '11', '74.6', '2', '1.8', '0.2', '31.6', '41',
'0.6', '2.8', '3', '3.6', '1.6', '7.6', '14', '13.4', '5.8', '5.2',
'8.4', '6.2', '21.4', '6.6', '40', '6.4', '2.6', '4', '8.2', '9.8',
'2.4', '3.8', '0.4', '1.2', '5', '17.4', '1.4', '5.4', '9.4',
'3.2', '0.8', '4.2', '2.2', '4.4', '14.8', '23.2', '30.8', '7.2',
'4.6', '32.8', '6', '3.4', '10.6', '19.8', '7.4', '13.8', '10.4',
'14.4', '26.8', '20', '16.6', '25.4', '7', '39.4', '20.2', '11.6',
'23', '27.6', '78.4', '32.4', '31', '47.2', '34.2', '32.2', '53.8',
'11.4', '9.6', '11.2', '15', '16.4', '60.8', '16.2', '33.2', '80',
'39.6', '25.6', '60.4', '24', '14.6', '16.8', '51.2', '20.6',
'52.6', '18.2', '13.6', '8', '9.2', '9', '41.2', '15.8', '24.2',
'44.8', '19', '45', '12.4', '44.6', '7.8', '61.8', '17.8', '29.2',
'24.4', '33.6', '25.2', '62.6', '32', '33', '24.8', '22.2', '22.4',
'4.8', '19.6', '28.8', '11.8', '18', '22', '20.4', '42.4', '45.8',
'28.6', '25.8', '21.8', '13.2', '34.8', '40.8', '23.6', '6.8',
'12.6', '17.6', '12.2', '26', '35', '19.2', '5.6', '49.8', '12.8',
'55', '60.2', '15.4', '10', '55.4', '30', '21.2', '40.2', '38',
'81.2', '39.8', '46.6', '54.8', '8.6', '102.8', '45.4', '38.8',
'31.4', '35.6', '51.8', '49', '21.6', '112', '33.8', '26.4',
'62.4', '25', '43.4', '77.8', '39.2', '18.6', '66.8', '18.4',
'38.6', '48.2', '41.6', '36.8', '37.4', '67.8', '40.4', '210.2',
'50.6', '29.8', '41.4', '76.2', '201.6', '12', '47', '10.8',
'84.8', '24.6', '69.2', '23.8', '29', '48.8', '56.8', '27.8', '50',
'28.2', '17.2', '33.4', '14.2', '42.2', '15.2', '68.6', '50.2',
'55.2', '37.6', '28', '8.8', '17', '54.4', '62.8', '15.6', '16',
'38.2', '57.4', '73.4', '27.2', '10.2', '37', '35.2', '20.8',
'27.4', '76', '42', '18.8', '99.2', '21', '51', '30.4', '26.6',
'22.8', '43.8', '49.4', '41.8', '49.2', '36.2', '23.4', '44.2',
'64', '51.4', '13', '55.8', '79.2', '84', '59.2', '28.4', '37.8',
'46.8', '36.4', '78', '19.4', '47.6', '63.6', '51.6', '89', '45.6',
'46.2', '92.2', '79.8', '37.2', '69.6', '29.6', '56.6', '34',
'74.2', '58.4', '44', '106', '58', '114.8', '61.4', '36', '57.2',
'32.6', '123', '53.6', '39', '71.6', '22.6', '59.8', '70.6',
'50.4', '26.2', '30.2', '62', '43.6', '64.8', '42.6', '78.2',
'58.8', '27', '56.2', '75.6', '31.2', '53.4', '94.6', '81.8',
'40.6', '106.2', '45.2', '43', '75', '66', '67', '64.4', '75.2',
'54.2', '74', '53.2', '95.8', '55.6', '34.4', '88.2', '52.2', '65',
'53', '77.2', '43.2', '92.8', '29.4', '57', '60.6', '66.2', '87.4',
'38.4', '82.2', '73.2', '46', '59.4', '104.2', '30.6', '102.6',
'48.4', '47.8', '132.6', '82.4', '80.4', '67.4', '88.8', '34.6',
'93.6', '57.6', '75.8', '35.4', '36.6', '79.4', '44.4', '81.4',
'139.8', '72.4', '99.6', '58.6', '31.8', '54', '84.6', '46.4',
'57.8', '65.4', '59.6', '56', '83.8', '71.8', '145.8', '80.2',
'64.6', '71', '50.8', '69', '66.4', '42.8', '93.8', '61.2', '88',
'189.8', '79.6', '85.2', '76.4', '76.8', '172.6', '125.6', '153.4',
'70.4', '48.6', '79', '67.6', '63.4', '99.4', '56.4', '115.6',
'58.2', '87.6', '65.2', '70.8', '96.4', '95.2', '67.2', '47.4',
'91.6', '105', '76.6', '68.8', '52.8', '61', '84.4', '93.2',
'90.4', '107', '48', '98.2', '95', '52.4', '52', '62.2', '77',
'73.6', '111.4', '73.8', '35.8', '90.8', '85.4', '91', '85',
'110.2', '68', '121', '89.6', '86.8', '77.6', '64.2', '68.2',
'109', '86', '100.8', '87.8', '71.2', '101.4', '63.8', '97.4',
'112.8', '97.2', '91.4', '65.8', '84.2', '63', '109.2', '129.2',
'95.4', '83.6', '72.6', '60', '88.6', '126', '93', '83.2', '128.2',
'110', '101.2', '95.6', '103.8', '96.8', '133', '113.8', '114',
'119', '59', '86.4', '63.2', '122', '118', '103.4', '54.6', '86.2',
'69.8', '74.4', '69.4', '72.2', '165.4', '49.6', '121.8', '78.8',
'77.4', '78.6', '86.6', '143', '82', '71.4', '70', '100.6', '66.6',
'180.4', '224.2', '82.8', '70.2', '120.6', '142.4', '177.4',
'114.6', '85.8', '113.2', '68.4', '61.6', '65.6', '127.6', '81',
'114.2', '89.2', '94.2', '152.4', '134.2', '94', '146.8', '103.2',
'83.4', '98', '104.6', '142', '114.4', '127.4', '153.6', '98.8',
'80.6', '138.4', '109.8', '87', '112.6', '96.2', '106.4', '133.8',
'91.2', '90', '90.2', '100.2', '146.6', '188.6', '83', '116.8',
'115.4', '143.4', '88.4', '111', '96', '107.6', '128.4', '166',
'102.4', '80.8', '130.6', '72', '98.6', '108', '94.8', '103.6',
'115.8', '101', '115', '139', '106.8', '119.6', '119.8', '89.8',
'119.2', '89.4', '81.6', '159.4', '116.6', '93.4', '108.8', '104',
'99', '72.8', '108.2', '134.6', '124.6', '112.4', '108.4', '104.4',
'91.8', '127.8', '122.2', '116.4', '118.4', '169.2', '169', '100',
'104.8', '87.2', '150', '111.8', '136', '108.6', '110.8', '179',
'113.4', '123.8', '141', '105.8', '147', '118.6', '74.8', '73',
'137.2', '149.8', '100.4', '128.6', '132.4', '115.2', '190.2',
'130', '92.6', '107.4'], dtype=object)
Ao vermos a lista de valores únicos de chuva podemos ver que há varios valores None, ou seja, por algum motivo a estação não registrou medição. Vale notar que também há o valor 0 nessa lista, então podemos assumir que o None seja algum erro da estação
Além disso podemos observar que a informação da lista está como strings, que deveram ser transformadas para realizarmos a análise
df['CHUVA'].fillna(value = np.nan, inplace = True)
df['CHUVA'] = df['CHUVA'].astype(float) #Transformando os dados da coluna em float
df
| UMID_MED | DT_MEDICAO | DC_NOME | UMID_MIN | TEMP_MED | CHUVA | VL_LATITUDE | TEMP_MIN | TEMP_MAX | UF | VEL_VENTO_MED | CD_ESTACAO | VL_LONGITUDE | MES | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | None | 2020-01-01 | ABROLHOS | None | 25.6 | NaN | -17.96305555 | 23.8 | 26.7 | BA | 6.9 | A422 | -38.70333333 | 01 |
| 1 | None | 2020-01-02 | ABROLHOS | None | 26.6 | 1.0 | -17.96305555 | 23.8 | 27.4 | BA | 10 | A422 | -38.70333333 | 01 |
| 2 | None | 2020-01-03 | ABROLHOS | None | 26.5 | 0.0 | -17.96305555 | 26 | 27.2 | BA | 9.9 | A422 | -38.70333333 | 01 |
| 3 | None | 2020-01-04 | ABROLHOS | None | 26 | 11.0 | -17.96305555 | 23.4 | 27.2 | BA | 8.9 | A422 | -38.70333333 | 01 |
| 4 | None | 2020-01-05 | ABROLHOS | None | 26.2 | 0.0 | -17.96305555 | 25.7 | 26.9 | BA | 9.6 | A422 | -38.70333333 | 01 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 221425 | 74.3 | 2020-12-27 | ZE DOCA | 52 | 27.9 | 0.0 | -3.26916666 | 24.1 | 33 | MA | None | A255 | -45.65111111 | 12 |
| 221426 | 75.7 | 2020-12-28 | ZE DOCA | 55 | 28.1 | 0.0 | -3.26916666 | 23.8 | 33.1 | MA | None | A255 | -45.65111111 | 12 |
| 221427 | 75.5 | 2020-12-29 | ZE DOCA | 53 | 28.1 | 0.0 | -3.26916666 | 23.7 | 33.8 | MA | None | A255 | -45.65111111 | 12 |
| 221428 | 82.6 | 2020-12-30 | ZE DOCA | 60 | 27 | 0.0 | -3.26916666 | 23.8 | 32.5 | MA | None | A255 | -45.65111111 | 12 |
| 221429 | 85.6 | 2020-12-31 | ZE DOCA | 57 | 26.6 | 8.8 | -3.26916666 | 24 | 33.3 | MA | None | A255 | -45.65111111 | 12 |
221430 rows × 14 columns
#Consolidando os dados somando os dados de chuva para termos a precipitação acumulada
df_soma = df.pivot_table(index = ['CD_ESTACAO','VL_LATITUDE','VL_LONGITUDE','UF'], aggfunc = {'CHUVA': 'sum'})
df_soma
| CHUVA | ||||
|---|---|---|---|---|
| CD_ESTACAO | VL_LATITUDE | VL_LONGITUDE | UF | |
| A001 | -15.78944444 | -47.92583332 | DF | 1585.0 |
| A002 | -16.642841 | -49.220222 | GO | 1469.4 |
| A003 | -17.745066 | -49.101698 | GO | 1338.2 |
| A005 | -13.30944444 | -49.11749999 | GO | 813.6 |
| A009 | -10.190744 | -48.301811 | TO | 1426.4 |
| ... | ... | ... | ... | ... |
| S713 | -22.0786111 | -53.46583333 | MS | 0.0 |
| S714 | -18.07277777 | -54.54888888 | MS | 242.4 |
| S715 | -20.466694 | -53.763028 | MS | 997.0 |
| S716 | -21.305889 | -52.820375 | MS | 902.8 |
| S717 | -20.35138888 | -51.43027777 | MS | 623.6 |
605 rows × 1 columns
df_soma_ord = df_soma.sort_values('CHUVA', ascending = False) #Ordenando os valores do maior para o menor
print('Resposta a questão 1:')
df_soma_ord.head(5)
Resposta a questão 1:
| CHUVA | ||||
|---|---|---|---|---|
| CD_ESTACAO | VL_LATITUDE | VL_LONGITUDE | UF | |
| A201 | -1.411228 | -48.439512 | PA | 4074.2 |
| A202 | -1.30083333 | -47.94805555 | PA | 3162.0 |
| A618 | -22.4486111 | -42.98694444 | RJ | 3156.8 |
| A248 | -1.73472221 | -47.0575 | PA | 2882.0 |
| A226 | -1.04722221 | -46.78583333 | PA | 2578.0 |
df_soma_ord.tail(5)
| CHUVA | ||||
|---|---|---|---|---|
| CD_ESTACAO | VL_LATITUDE | VL_LONGITUDE | UF | |
| A133 | -5.7886111 | -61.28833333 | AM | 0.0 |
| B803 | -24.57083332 | -52.80027777 | PR | 0.0 |
| A751 | -23.96694443 | -55.02416666 | MS | 0.0 |
| A905 | -13.78583333 | -57.8386111 | MT | 0.0 |
| A765 | -23.84472221 | -46.14333333 | SP | 0.0 |
As estações acima seriam as que menos tiveram precipitação acumulada ao longo de 2020, como o resultado é 0 é interessante saber quais estações tiveram uma precipitação acumulada de 0, já que não haveria distinção entre elas
df_soma_ord[df_soma_ord['CHUVA'] == 0.0]
| CHUVA | ||||
|---|---|---|---|---|
| CD_ESTACAO | VL_LATITUDE | VL_LONGITUDE | UF | |
| A908 | -14.01638888 | -52.21166666 | MT | 0.0 |
| S113 | 0.947942 | -59.91318 | RR | 0.0 |
| A415 | -11.00277777 | -44.52499999 | BA | 0.0 |
| S116 | -4.530149 | -71.617366 | AM | 0.0 |
| S115 | 0.599381 | -69.191946 | AM | 0.0 |
| ... | ... | ... | ... | ... |
| A133 | -5.7886111 | -61.28833333 | AM | 0.0 |
| B803 | -24.57083332 | -52.80027777 | PR | 0.0 |
| A751 | -23.96694443 | -55.02416666 | MS | 0.0 |
| A905 | -13.78583333 | -57.8386111 | MT | 0.0 |
| A765 | -23.84472221 | -46.14333333 | SP | 0.0 |
66 rows × 1 columns
Como podemos ver, 66 estações não tiveram chuva ao longo de 2020. Abaixo podemos vizualizar as localizações dessas estações
df_loc = df_soma_ord.reset_index()
df_loc = df_loc[df_loc['CHUVA']== 0.0]
mapa = requests.get('https://raw.githubusercontent.com/fititnt/gis-dataset-brasil/master/uf/geojson/uf.json')
fig = px.scatter_geo(df_loc,
lat=df_loc['VL_LATITUDE'],
lon=df_loc['VL_LONGITUDE'],
hover_data=["CD_ESTACAO",'UF'],
scope = 'south america',
width = 800,
height = 1000
)
fig.show()
O mapa acima chama atenção pelos diversos pontos espalhados, valendo a pena ver como os dados foram obtidos pois parece ser algo incomum
df['TEMP_MED'] = df['TEMP_MED'].astype(float) #Transformando os dados da coluna em float
#Abaixo irei consolidar os dados de acordo com o estado e o mês, pegando a média das temperaturas médias
df_temp = df.pivot_table(index = ['UF','MES'], aggfunc = {'TEMP_MED': 'mean'})
df_temp = df_temp.reset_index()
#Abaixo irei ordernar os valores por Estado e Mês, depois vou trocar os nomes dos meses, para plotar um gráfico melhor
df_temp = df_temp.sort_values(['UF','MES']).replace({'01': 'JAN', '02': 'FEV', '03': 'MAR',
'04': 'ABR', '05': 'MAI', '06': 'JUN',
'07': 'JUL', '08': 'AGO', '09': 'SET',
'10': 'OUT', '11': 'NOV', '12': 'DEZ'})
fig = px.bar(df_temp,x = 'MES', y = 'TEMP_MED', barmode = 'group', animation_frame = 'UF', hover_name = 'UF',
labels = {'TEMP_MED':'Temperatura Média'}, title = 'Temperatura Média por Estado',
color = 'TEMP_MED', color_continuous_scale = 'bluered',
range_y = (0,30),
range_color = [df_temp['TEMP_MED'].min(), df_temp['TEMP_MED'].max()]
)
print('Resposta da questão 3:')
fig.show()
Resposta da questão 3:
Optei por plotar apenas um gráfico interativo para cada estado, acredito que seja uma opção mais elegante do que plotar 27 gráficos diferentes para cada estado
Também podemos ter essa mesma representação em um mapa coroplético
figbr = px.choropleth_mapbox(df_temp , geojson=mapa.json(),
locations='UF',
featureidkey="properties.UF_05",
animation_frame = 'MES',
color = 'TEMP_MED',
color_continuous_scale="bluered",
mapbox_style="carto-positron",
zoom=2, center = {"lat": -16.6799, "lon": -49.255},
opacity=0.7,
range_color = [df_temp['TEMP_MED'].min(), df_temp['TEMP_MED'].max()]
)
figbr.update_layout(margin={"r":0,"t":0,"l":0,"b":0})
figbr.show()